//== SAGITTARIUS ============================================================================

//== FILEDOC =========================================================================

/** @file typetable.h
  * @brief The type table
  */

//== BEGINNING OF CODE ===============================================================

#ifndef __SAGUTTARUIS__TYPETABLE__H_
#define __SAGUTTARUIS__TYPETABLE__H_

//== INCLUDES ========================================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/parallel/multisema.h"
#include "sagittarius/lexer/lexcore.h"
#include "sagittarius/symtab/symtab.h"

/// Needed to get mixing
#define TTABLE_MORPH_HASH(k) (k >> 8)

#ifdef __cplusplus
extern "C" {
#endif

#if 0
typedef enum {
    /// Empty object
    SG_BUILTIN_TYPE_EMP,
    /// Dynamic object
    SG_BUILTIN_TYPE_OBJ,
    /// A reference to another object
    SG_BUILTIN_TYPE_REF,
    /// An integer
    SG_BUILTIN_TYPE_INT,
    /// A real
    SG_BUILTIN_TYPE_REAL,
    /// A string
    SG_BUILTIN_TYPE_STR,
    /// A vector
    /*SG_BUILTIN_TYPE_VEC,
    /// A tuple
    SG_BUILTIN_TYPE_TUP, //note: type depends on elt types
    /// A hashmap
    SG_BUILTIN_TYPE_HASH,*/
    /// empty
    //SG_BUILTIN_TYPE_EMP,
    /// nil
    SG_BUILTIN_TYPE_NIL,
    /// Sentinel
    SG_BUILTIN_TYPE_END
} type_builtins;

extern const char* type_builtinstr[SG_BUILTIN_TYPE_END];
#endif

//Field of a type
/*typedef struct {
    sg_fieldType type;
    uint64 offset; //in bytes
} fieldrec;*/


#define OB_FLAG_OBJ  0x0001
#define OB_FLAG_INT  0x0002
#define OB_FLAG_REAL 0x0004
#define OB_FLAG_STR  0x0008
#define OB_FLAG_VEC  0x0010
#define OB_FLAG_TUP  0x0020
#define OB_FLAG_HASH 0x0040

typedef uint64 typecode;

/*typedef union {
    typecode k;
    uint8 u;
} typehash;*/

/// Shift count
#define TYPECODE_CNT_SHIFT (64-8)
#define TYPECODE_CNT_MASK 0xFF00000000000000
#define TYPECODE_CNT_ANTIMASK 0x00FFFFFFFFFFFFFF

typecode typecode_set_cnt(typecode t, uint8 c);
uint8 typecode_get_cnt(typecode t);

//#define TYPECODE_SET_CNT(t,c) t &= TYPECODE_CNT_ANTIMASK; t |= c

//typedef uint64 typid;

typedef enum {
    FF_INT32,
    FF_INT64,
    FF_UINT32,
    FF_UINT64,
    FF_FP32,
    FF_FP64
} FIELDTYPE;

#define FF_FIELD_SHIFT 32
#define FF_FIELD_MASK 0x00000000FFFFFFFF

typedef struct {
    /// Typecode
    uint64 t;
    /// Offset
    uint32 i;
    /// Field bits
    // upper bits: type of field
    // lower bits: offset of field
    //uint64 f;
} fieldrec;

int fieldrec_cmp(fieldrec* a, fieldrec* b);

typedef struct {
    uint32 n;
    uint32 c;
    fieldrec* f;
} typedesc;

int typedesc_cmp(typedesc* a, typedesc* b);

void typedesc_push_field(typedesc* d, fieldrec f);

//Type record
typedef struct {
    typecode id;
    //LexBuf rep;
    char* rep;
    typedesc* d;
    // TODO: add option for symbol-based types?
    int sym_based;
    symcode s;
    /// Is the type built-in?
    //int builtin;
    /// Reference to @ref type_builtins if the type is built-in
    //uint32 builtinref;
    /// Number of fields
    //uint32 nf;
    /// Size
    //uint64 sz;
    /// Offset
    //uint32 o;
    /// Number of field buckets
    //uint32 n;
    /// Depth of field buckets
    //uint32 d;
    /// # bucket elts
    //uint32* c;
    /** @brief Field offset buckets
     * @details First element of each bucket is # elts in bucket
     */
    //fieldrec* f;
    //uint32 n_refs; //for gc
} typerec;

typedef struct {
    /// Number of records
    uint64 n;
    uint64 depth;
    /// Records
    typerec** e;
    //SG_DEF_MULTISEMA(sema);
} ttable_bucket;

typedef struct {
    /// Record capacity
    //uint64 c;
    /// Number of records
    uint64 nr;
    /// Number of buckets
    uint64 nb;
    /// Buckets
    ttable_bucket* b;
    /// Records
    //typerec** e;
    SG_DEF_MULTISEMA(sema);
} typetable;

typedef struct {
    /// The id, if found
    typehash id;
    /// True if it already exists in the table
    int b;
} ttablecheck;

typedef struct {
    /// The record, if found
    typerec* t;
    /// True if it already exists in the table
    int b;
} ttablequery;

// -- Type Record --

//typerec* make_ttable_record(typetable* st, constLexBuf name);

//C_INLINE typerec make_ttable_record_codel(typetable* st, typecode k, LexBuf rep, size_t l);

/// Rehash the fields
fieldrec* typeref_fields_rehash(fieldrec* f, uint32 n, uint32 depth);

/// Initialize the fields
void typeref_init_fields(typerec* p);

/// Add a field to the type (you can't do this online and call it the same type)
void typerec_add_field(typerec* p, uint64 sym, uint64 typecode, size_t bytes);

typetable* create_type_table();
void init_type_table(typetable* tt);
void finalize_type_table(typetable* tt);

int ttable_new_symtype(typetable* tt, typecode t);

int ttable_install_type(typedesc* td);

//uint8 ttable_count_k(typetable* st, typehash id, constLexBuf name);

/// Like @ref ttable_count_k but with explicit length, can be used for temporaries
//uint8 ttable_count_builtinl(typetable* st, typehash id, constLexBuf name, size_t l);

//typerec* ttable_query(typetable* tt, typehash e);

//typerec* ttable_check_namel(typetable* tt, constLexBuf name, size_t l);

/// Ephemeral
//typerec* ttable_check_namel_eph(typetable* tt, constLexBuf name, size_t l);

//TODO: add capability to get field offset by symbol (& index?)

/// Insert a new type
//int _ttable_insert(typetable* tt, typerec e);

/// Get a type corresponding to the symbol; If it does not exist, create it
//typehash ttable_get_or_insertl(typetable* tt, constLexBuf name, size_t l);

//typetable* ttable_rehash(typetable* tt, uint64 nbuckets, uint64 depth);

void ttablePrintSym(int fd, typetable* tt, typehash e);

void typetable_dumpstate(int fd, typetable* h);

#ifdef __cplusplus
}//extern "C"
#endif

#endif
